﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpStructureProfileSearchUtils.h"
#include "IPolygon.h"
#include "IPolyCurve.h"
#include "ICurve2d.h"
#include "IArc2d.h"
#include "ILine2d.h"
#include "IBody.h"
#include "Intervald.h"
#include "ICurve2dCurve2dIntersection.h"
#include "Box3d.h"
#include "AlgorithmPositionJudge.h"
#include "AlgorithmPolygonOperate.h"
#include "AlgorithmIntersect.h"
#include "AlgorithmProperty.h"
#include "AlgorithmProject.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

Vector2d GbmpStructureProfileSearchUtils::Vector2dRoatate90ClockWise(const Vector2d& vec)
{
    return Vector2d(vec.Y(), -vec.X());
};

bool gcmp::GbmpStructureProfileSearchUtils::IsLoop1InLoop2(const IPolyCurve* pLoop1, const IPolyCurve* pLoop2)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pLoop1 && pLoop2 && pLoop1->GetCurveCount() > 0 && pLoop2->IsValid(), L"pLoop1与pLoop2不能为空",L"GDMPLab",L"2024-03-30");
    const Vector2d pointOnLoop1 = pLoop1->GetCurve(0)->GetStartPoint();
    OwnerPtr<IPolygon> opPolygon = IPolygon::Create();
    opPolygon->AddPolyCurveByClone(pLoop2);
     PointPolygonPositionTypes type = AlgorithmPositionJudge::GetPosition(pointOnLoop1, opPolygon.get(), Constants::LENGTH_EPS);    return PointPolygonPositionTypes::Inside == type;
}

bool gcmp::GbmpStructureProfileSearchUtils::BoolOperateUnite(const std::vector<OwnerPtr<IPolygon>>& polygons, OwnerPtr<IPolygon>& result)
{
    if (polygons.empty())
        return false;
    OwnerPtr<IPolygon> thisResult = TransferOwnershipCast<IPolygon>(polygons[0]->Clone());

    for (int i = 1; i < polygons.size(); i++)
    {
        OwnerPtr<IPolygon> tmp;
        bool ok = AlgorithmPolygonOperate::BoolUnite(thisResult.get(), polygons[i].get(), tmp);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(ok && tmp.get(), L"布尔运算失败",L"GDMPLab",L"2024-03-30");
        thisResult = TransferOwnership(tmp);
    }
    result = TransferOwnership(thisResult);
    return true;
}

gcmp::OwnerPtr<gcmp::IPolygon> gcmp::GbmpStructureProfileSearchUtils::ProjectBodiesAndBoolUnite(
    const std::vector<const IBody*>& bodies,
    const Coordinate3d& coord)
{
    if (bodies.empty())
        return nullptr;
    
    std::vector<OwnerPtr<IPolygon>> polygons;
    FOR_EACH(gBody, bodies)
    {
        OwnerPtr<IPolygon> opPolygon = AlgorithmProject::Project(coord, gBody);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opPolygon.get(), L"投影失败",L"GDMPLab",L"2024-03-30");
        polygons.emplace_back(TransferOwnership(opPolygon));
    }

    if (polygons.empty())
        return nullptr;

    OwnerPtr<IPolygon> result;
    bool ok = GbmpStructureProfileSearchUtils::BoolOperateUnite(polygons, result);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(ok && result.get(), L"BoolOperateUnit失败",L"GDMPLab",L"2024-03-30");

    return result;
}
 
double gcmp::GbmpStructureProfileSearchUtils::GetLoopArea(const IPolyCurve* pLoop)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pLoop && pLoop->IsValid() && pLoop->IsAnticlockwise(), L"pLoop不为空且pLoop必须是逆时针",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IPolygon> opPolygon = IPolygon::Create();
    opPolygon->AddPolyCurveByClone(pLoop);

    return AlgorithmProperty::GetArea(opPolygon.get());
}

bool gcmp::GbmpStructureProfileSearchUtils::Curve1EndConnectCurve2Start(ICurve2d* pFirst, ICurve2d* pSecond, bool autoAdjustRangeMinMax)
{
    OwnerPtr<ICurve2d> opCurCurve = TransferOwnershipCast<ICurve2d>(pFirst->Clone());
    OwnerPtr<ICurve2d> opNextCurve = TransferOwnershipCast<ICurve2d>(pSecond->Clone());


    double len1 = -1;
    {
        IArc2d* pArc1 = dynamic_cast<IArc2d*>(pFirst);
        if (pArc1)
        {

            len1 = 2 * gcmp::Constants::MATH_PI * pArc1->GetRadius() - pFirst->GetLength();
            if (len1 > 0)
                len1 /= 2;
        }
        if (len1 < 0)

            len1 = Constants::MAX_MODEL_SPACE_SIZE;
    }

    double len2 = -1;
    {
        IArc2d* pArc2 = dynamic_cast<IArc2d*>(pSecond);
        if (pArc2)
        {

            len2 = 2 * gcmp::Constants::MATH_PI * pArc2->GetRadius() - pSecond->GetLength();
            if (len2 > 0)
                len2 /= 2;
        }
        if (len2 < 0)

            len2 = Constants::MAX_MODEL_SPACE_SIZE;
    }
   

    opCurCurve->SetLimits(Intervald(opCurCurve->GetStartLimit(), opCurCurve->GetEndLimit() + len1));
    opNextCurve->SetLimits(Intervald(opNextCurve->GetStartLimit() - len2, opNextCurve->GetEndLimit()));

    const double END_PARAM = pFirst->GetEndLimit();

    std::vector<OwnerPtr<ICurve2dCurve2dIntersection>> opIntersects;
    bool intersect = AlgorithmIntersect::Intersect(opCurCurve.get(), opNextCurve.get(), opIntersects, Constants::DISTANCE_ITERATION_EPS);
    if (!intersect)
    {
        return false;
    }
    else
    {
        double p1 = 0.0;
        double p2 = 0.0;

        double min = Constants::MATH_INFINITY;

        // 相交
        if (CurveCurveIntersectTypes::CurveCurveNormalType == opIntersects[0]->GetIntersectType())
        {
            p1 = opIntersects[0]->GetParamOnFirstCurve();
            p2 = opIntersects[0]->GetParamOnSecondCurve();
            double min = fabs(p1 - END_PARAM);
            FOR_EACH(opIntersect, opIntersects)
            {
                double t1 = opIntersect->GetParamOnFirstCurve();
                double sub = fabs(t1 - END_PARAM);
                if (sub < min)
                {
                    min = sub;
                    p1 = t1;
                    p2 = opIntersect->GetParamOnSecondCurve();
                }
            }
        }
        // 重合
        else if (CurveCurveIntersectTypes::CurveCurveCoincidenceType == opIntersects[0]->GetIntersectType())
        {
            const ICurve2dCoincidenceData* pData = opIntersects[0]->GetCurve2dCoincidenceData();
            std::vector<Intervald> intervals = pData->GetIntervalds();
            p1 = intervals[0].GetMin();
            p2 = intervals[1].GetMin();
            Vector2d s1 = opCurCurve->GetPoint(p1);
            Vector2d s2 = opNextCurve->GetPoint(p2);

            if (!(s1 - s2).IsZero(Constants::LENGTH_EPS))
            {
                p2 = intervals[1].GetMax();
            }
        }
        else
        {
            DBG_WARN_AND_RETURN_FALSE_UNLESS(false, L"不应该是此求交类型",L"GDMPLab",L"2024-03-30");
        }

        Intervald range1 = opCurCurve->GetLimits();
        if (p1 > range1.GetMin())
        {
            range1.Set(range1.GetMin(),p1);
        }
        else
        {
            if (autoAdjustRangeMinMax)
            {
                range1.Set(p1, range1.GetMin());
            }
            else
            {
                return false;
            }

        }
        opCurCurve->SetLimits(range1);

        Intervald range2 = opNextCurve->GetLimits();
        if (p2 < range2.GetMax())
        {
            range2.Set(p2, range2.GetMax());
        }
        else
        {
            if (autoAdjustRangeMinMax)
            {
                range2.Set(p2, range2.GetMax());
            }
            else
            {
                return false;
            }
        }
        opNextCurve->SetLimits(range2);

        pFirst->SetLimits(range1);
        pSecond->SetLimits(range2);

        return true;
    }
}

bool gcmp::GbmpStructureProfileSearchUtils::GetAllDisconnectedOutterLoop(const IPolygon* pPolygon, std::vector<OwnerPtr<IPolyCurve>>& result, double* pMinArea)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pPolygon, L"pPolygon为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pPolygon->GetPolyCurveCount() > 0, L"pPolygon为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_FALSE_UNLESS(result.empty(), L"result不为空",L"GDMPLab",L"2024-03-30");
    std::vector<OwnerPtr<IPolyCurve>> loops;
    for (int i = 0; i < pPolygon->GetPolyCurveCount(); i++)
    {
        OwnerPtr<IPolyCurve> opLoop = pPolygon->GetPolyCurve(i);
        if (opLoop->IsAnticlockwise())
        {

            if (nullptr == pMinArea || MathUtils::IsGreaterThan(GbmpStructureProfileSearchUtils::GetLoopArea(opLoop.get()), *pMinArea, Constants::LENGTH_EPS))
            {
                loops.emplace_back(TransferOwnership(opLoop));
            }
        }
    }

    // 去除loop中的loop
    for (int i = 0; i < (int)loops.size();)
    {
        bool shouldRemoveI = false;
        for (int j = i + 1; j < (int)loops.size(); j++)
        {
            IPolyCurve* pLoop1 = loops[i].get();
            IPolyCurve* pLoop2 = loops[j].get();
            if (!pLoop1->GetBox().IsIntersect(pLoop2->GetBox()))
            {
                continue;
            }

            if (GbmpStructureProfileSearchUtils::IsLoop1InLoop2(pLoop2, pLoop1))
            {
                loops.erase(loops.begin() + j);
                j--;
                continue;
            }
            else if (GbmpStructureProfileSearchUtils::IsLoop1InLoop2(pLoop1, pLoop2))
            {
                shouldRemoveI = true;
                break;
            }
            else
            {
                // 相离
            }
        }
        if (shouldRemoveI)
        {
            loops.erase(loops.begin() + i);
        }
        else
        {
            result.emplace_back(TransferOwnership(loops[i]));
            i++;
        }
    }

    return true;
}


void gcmp::GbmpStructureProfileSearchUtils::PolygonToCurveGroup(std::vector<OwnerPtr<ICurve2d>>& allCurve, double howLongIsNoise, bool arcIsNotNoise, std::vector<OwnerPtr<CurveOffsetNode>> &groups)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(!allCurve.empty(), L"allCurve为空",L"GDMPLab",L"2024-03-30");

    int curveCount = (int)allCurve.size();
    for (int i = 0; i < curveCount; i++)
    {
        OwnerPtr<ICurve2d>& opCurve = allCurve[i];
        bool isLine = nullptr != dynamic_cast<ILine2d*>(opCurve.get());
        if (arcIsNotNoise && !isLine)
        {
            // 若不认为圆弧是噪声，则单独添加到非噪声节点
            OwnerPtr<CurveOffsetNode> opCurItem = NEW_AS_OWNER_PTR(CurveOffsetNode);
            opCurItem->opCurve = TransferOwnership(opCurve);

            groups.emplace_back(TransferOwnership(opCurItem));
            continue;
        }
        double len = opCurve->GetLength();
        // 不小于
        if (!MathUtils::IsLessThan(len, howLongIsNoise, Constants::DOUBLE_EPS))
        {
            // 当前节点
            OwnerPtr<CurveOffsetNode> opCurItem = NEW_AS_OWNER_PTR(CurveOffsetNode);
            opCurItem->opCurve = TransferOwnership(opCurve);

            groups.emplace_back(TransferOwnership(opCurItem));
        }
        else
        {
            int tailIdx = (int)groups.size() - 1;
            if (groups.empty() || groups[tailIdx]->noiseVector.empty())
            {
                OwnerPtr<CurveOffsetNode> opCurItem = NEW_AS_OWNER_PTR(CurveOffsetNode);
                groups.emplace_back(TransferOwnership(opCurItem));
                tailIdx = (int)groups.size() - 1;
            }
            if (isLine)
            {
                groups[tailIdx]->noiseVector.emplace_back(TransferOwnership(opCurve));
            }
            else  // 圆弧是凹凸，则要用弦来代替
            {
                // 若认为圆弧是噪声，则将圆弧用直线表示，并添加到噪声节点中
                Vector2d startPt = opCurve->GetStartPoint();
                Vector2d endPt = opCurve->GetEndPoint();
                OwnerPtr<ILine2d> opNewCurve = ILine2d::Create(startPt, endPt);
                groups[tailIdx]->noiseVector.emplace_back(TransferOwnership(opNewCurve));
            }
        }
    }

    //  第一组和最后一组都是凹凸的话，要合并在一起
    int lastGroupIdx = (int)groups.size() - 1;
    if (lastGroupIdx > 0
        && (!groups[0]->opCurve.get() && !groups[lastGroupIdx]->opCurve.get()))
    {
        FOR_EACH_INDEX(i, groups[0]->noiseVector)
        {
            OwnerPtr<ICurve2d>& opCurve = groups[0]->noiseVector[i];
            groups[lastGroupIdx]->noiseVector.emplace_back(TransferOwnership(opCurve));
        }
        groups.erase(groups.begin());
    }
}
